<?php
//
// ACL 管理器
////////////////////////////////////////

class Acl_Mgr
{
    const _CONST_ACL_OBJ_KEY_  = 'acl_instance';
	const _ACL_ROLES		   = 'user_role_define';
	const _ACL_RESOURCES	   = 'acl_resources';
	const _ACL_RULES		   = 'acl_rules';
	const _DB_ACL			   = 'acl';
    protected static $_instance;


    public static function instance ()
    {
        if (is_null(self::$_instance))
        {
            self::$_instance = self::create_acl ();
        }

        return self::$_instance;
    }

	/*
	 * reload acl rules & rebuild ACL instance 
	 *
	 * @return	ACL		ACL instannce
	 */
	public static function reload ()
	{
		$key   = self::_CONST_ACL_OBJ_KEY_;
        $cache = Cache::instance();
		$cache->delete($key);

		self::$_instance = NULL;
		return self::instance();
	}

    protected static function create_acl ()
    {
        $key   = self::_CONST_ACL_OBJ_KEY_;
        $cache = Cache::instance();
        $acl   = $cache->get($key);

        if (empty($acl))
        {
            $acl = self::build_acl_core();
            //$cache->set($key, $acl, Kohana::config('acl_config.timeout.rules'));

			$cache->set($key, $acl, 'acl', Kohana::config('acl_config.timeout.rules'));
        }

        return $acl;
    }

	private static function acl_roles()
	{
		$query	= DB::select()
					->from(self::_ACL_ROLES)
					->execute(self::_DB_ACL);

		$rs		= $query->as_array();
		$data	= '';
		for($i=0; $i<count($rs);$i++)
		{
			$data[$i][] = $rs[$i]['role_name'];

			$parent_id	= $rs[$i]['parentid'];
			
			$data[$i][]  = self::acl_role_name($parent_id);
		}
		return $data;
	}

	private static function acl_resources()
	{ 
		$rs	= DB::select('name')
					->from(self::_ACL_RESOURCES) 
					->execute(self::_DB_ACL);
		$data	= '';

		for($i=0; $i< count($rs); $i++)
		{
			$data[] = $rs[$i]['name'];
		}
		return $data;
	}

	private static function acl_role_name($role_id)
	{
		if(intval($role_id) < 0)
			return null;

		$query	= DB::select()
					->from(self::_ACL_ROLES) 
					->where('id','=',$role_id)
					->execute(self::_DB_ACL);

		$rs		= $query->as_array();

		return (empty($rs)) ? null : $rs[0]['role_name'];
	}

	private static function acl_resource_name($resource_id)
	{
		if(intval($resource_id) < 0)
			return null;

		$query	= DB::select()
					->from(self::_ACL_RESOURCES)
					->where('id','=',$resource_id)
					->execute(self::_DB_ACL);

		$rs		= $query->as_array();

		return (empty($rs)) ? null : $rs[0]['name'];

	}

	private static function acl_rules()
	{
		$rules  = array('allow' => array(), 'deny' => array());

		$query  = DB::select('r.*', 'u.role_name', array('res.name', 'res_name'))
			->from(array(self::_ACL_RULES, 'r'))
			->join(array(self::_ACL_RESOURCES, 'res'), 'LEFT')
			->on('r.resource_id', '=', 'res.id')
			->join(array(self::_ACL_ROLES, 'u'), 'LEFT')
			->on('r.role_id', '=', 'u.id')
			->execute(self::_DB_ACL)
			->as_array();

		foreach ($query as $r)
		{
			$type     = $r['type'];
			$resource = $r['res_name'];
			$role     = $r['role_name'];
			$method   = $r['method'];
			$o        = $method;
			$method   = empty($method) ? 
						NULL :
						(FALSE === strpos($method, ',') ? 
								$method : 
								explode(',', $method));

			if (!empty($r['assert']) && class_exists((string) $r['assert'])) 
				$assert = new $r['assert'];
			else 
				$assert = NULL;

			if (empty($resource))
				$rule = array($role);
			else 
			{ 
				if (empty($method))
					$rule = array($role, $resource);
				else 
				{
					if (empty($assert))
						$rule = array($role, $resource, $method);
					else
						$rule = array($role, $resource, $method, $assert);
				}
			}

			$rules[$type][] = $rule;
		}

		return $rules;
	}

    protected static function build_acl_core ()
    {
        $acl = Acl::instance();

        $roles     = self::acl_roles();
        $resources = self::acl_resources();
        $rules     = self::acl_rules();
        foreach ($roles as $role_name)
			$acl->add_role($role_name[0],array($role_name[1]));
        
		foreach ($resources as $res)
			$acl->add_resource ($res);

        foreach ($rules['allow'] as $rule)
        {
            switch (count($rule))
            {
            case 1:
                $acl->allow($rule[0]);
                break;

            case 2:
                $acl->allow($rule[0], $rule[1]);
                break;

            case 3:
                $acl->allow($rule[0], $rule[1], $rule[2]);
                break;

            case 4:
                $acl->allow($rule[0], $rule[1], $rule[2], $rule[3]);
                break;
            }
        }

        foreach ($rules['deny'] as $rule)
        {
            switch (count($rule))
            {
            case 1:
                $acl->deny($rule[0]);
                break;

            case 2:
                $acl->deny($rule[0], $rule[1]);
                break;

            case 3:
                $acl->deny($rule[0], $rule[1], $rule[2]);
                break;
            }
        }

        return $acl;
    }
}
